@(branch: String,
  repository: gitbucket.core.service.RepositoryService.RepositoryInfo,
  pathList: List[String],
  content: gitbucket.core.util.JGitUtil.ContentInfo,
  latestCommit: gitbucket.core.util.JGitUtil.CommitInfo,
  hasWritePermission: Boolean,
  isBlame: Boolean,
  isLfsFile: Boolean,
  tabSize: Int,
  highlighterTheme: String)(implicit context: gitbucket.core.controller.Context)
@import gitbucket.core.view.helpers
@gitbucket.core.html.main(s"${(repository.name :: pathList).mkString("/")} at ${branch} - ${repository.owner}/${repository.name}", Some(repository), highlighterTheme) {
  @gitbucket.core.html.menu("files", repository){
    <style>
      .prettyprint {
        tab-size: @tabSize
      }
    </style>
    <div class="head">
      <div class="pull-right hide-if-blame"><div class="btn-group">
        <a href="@helpers.url(repository)/blob/@helpers.encodeRefName((latestCommit.id :: pathList).mkString("/"))" data-hotkey="y" style="display: none;">Transfer to URL with SHA</a>
        <a href="@helpers.url(repository)/find/@helpers.encodeRefName(branch)" class="btn btn-sm btn-default" data-hotkey="t">Find file</a>
      </div></div>
      <div class="line-age-legend">
        <span>Newer</span>
        <ol>
            <li class="heat1"></li>
            <li class="heat2"></li>
            <li class="heat3"></li>
            <li class="heat4"></li>
            <li class="heat5"></li>
            <li class="heat6"></li>
            <li class="heat7"></li>
            <li class="heat8"></li>
            <li class="heat9"></li>
            <li class="heat10"></li>
        </ol>
        <span>Older</span>
      </div>
      <div id="branchCtrlWrapper" style="display:inline;">
      @gitbucket.core.helper.html.branchcontrol(branch, repository, hasWritePermission){
        @repository.branchList.map { x =>
          <li><a href="@helpers.url(repository)/blob/@helpers.encodeRefName((x :: pathList).mkString("/"))">@gitbucket.core.helper.html.checkicon(x == branch) @x</a></li>
        }
      }
      </div>
      <a href="@helpers.url(repository)/tree/@helpers.encodeRefName(branch)">@repository.name</a> /
      @pathList.zipWithIndex.map { case (section, i) =>
        @if(i == pathList.length - 1){
          @section
        } else {
          <a href="@helpers.url(repository)/tree/@helpers.encodeRefName((branch :: pathList.take(i + 1)).mkString("/"))">@section</a> /
        }
      }
      @if(isLfsFile){
        <span class="label label-info">LFS</span>
      }
    </div>
    <div class="box-header" style="line-height: 28px;">
      @helpers.avatarLink(latestCommit, 20)
      @helpers.user(latestCommit.authorName, latestCommit.authorEmailAddress, "username strong")
      <span class="muted">@gitbucket.core.helper.html.datetimeago(latestCommit.commitTime)</span>
      <span class="label label-default">@helpers.readableSize(content.size)</span>
      <a href="@helpers.url(repository)/commit/@latestCommit.id" class="commit-message">@helpers.link(latestCommit.summary, repository)</a>
      <div class="btn-group pull-right">
        <a class="btn btn-sm btn-default" href="@helpers.url(repository)/raw/@latestCommit.id/@helpers.encodeRefName(pathList.mkString("/"))">Raw</a>
        @if(content.viewType == "text"){
          <a class="btn btn-sm btn-default blame-action" href="@helpers.url(repository)/blame/@latestCommit.id/@helpers.encodeRefName(pathList.mkString("/"))"
            data-url="@helpers.url(repository)/get-blame/@helpers.encodeRefName((latestCommit.id :: pathList).mkString("/"))" data-repository="@helpers.url(repository)">Blame</a>
        }
        <a class="btn btn-sm btn-default" href="@helpers.url(repository)/commits/@helpers.encodeRefName((branch :: pathList).mkString("/"))">History</a>
        @if(hasWritePermission && content.viewType == "text" && repository.branchList.contains(branch)){
          <a class="btn btn-sm" style="padding-right: 4px;" href="@helpers.url(repository)/edit/@helpers.encodeRefName((branch :: pathList).mkString("/"))"><i class="octicon octicon-pencil" aria-label="Edit"></i></a>
        }
        @if(hasWritePermission && repository.branchList.contains(branch)){
          <a class="btn btn-sm" style="padding-right: 4px;" href="@helpers.url(repository)/remove/@helpers.encodeRefName((branch :: pathList).mkString("/"))"><i class="octicon octicon-trashcan" aria-label="Remove"></i></a>
        }
      </div>
    </div>
    @defining(helpers.isRenderable(pathList.last)){ isRenderable =>
      @if(!isBlame && isRenderable) {
        <div class="box-content-bottom @if(content.viewType == "text"){ markdown-body } " style="padding-left: 20px; padding-right: 20px;">
          @helpers.renderMarkup(pathList, content.content.getOrElse(""), branch, repository, false, false, true)
        </div>
      }else{
        @if(content.viewType == "text"){
          <div class="box-content-bottom">
            <pre class="prettyprint linenums blob @if(!isRenderable){ no-renderable } ">@content.content.map(_.replaceAll("^(\r?\n)", "$1$1"))</pre>
          </div>
        }
        @if(content.viewType == "image"){
          <div class="box-content-bottom">
            <img src="@helpers.url(repository)/raw/@helpers.encodeRefName((branch :: pathList).mkString("/"))"/>
          </div>
        }
        @if(content.viewType == "large" || content.viewType == "binary"){
          <div class="box-content-bottom" style="text-align: center; padding-top: 20px; padding-bottom: 20px;">
            <a href="@helpers.url(repository)/raw/@helpers.encodeRefName((branch :: pathList).mkString("/"))">View Raw</a><br>
            <br>
            (Sorry about that, but we can't show files that are this big right now)
          </div>
        }
      }
    }
  }
}
<script>
$(window).on('load', function(){
  window.onhashchange = function(){
    updateHighlighting();
  }

  const pre = $('pre.prettyprint');
  function updateSourceLineNum() {
    $('.source-line-num').remove();
    const pos = pre.find('ol.linenums').position();
    if (pos) {
      $('<div class="source-line-num">').css({
        height  : pre.height(),
        width   : '48px',
        cursor  : 'pointer',
        position: 'absolute',
        top     : pos.top + 'px',
        left    : pos.left + 'px'
      }).click(function(e){
        let pos = $(this).data("pos");
        if (!pos) {
          pos = $('ol.linenums li').map(function(){ return { id: $(this).attr("id"), top: $(this).position().top} }).toArray();
          $(this).data("pos",pos);
        }
        let i = 0;
        for(i = 0; i < pos.length - 1; i++){
          if(pos[i + 1].top > e.pageY){
            break;
          }
        }
        const line    = pos[i].id.replace(/^L/,'');
        const hash    = location.hash;
        const baseUrl = location.toString().split("#")[0];
        if (e.shiftKey == true && hash.match(/#L\d+(-L\d+)?/)) {
          const fragments = hash.split('-');
          window.history.pushState('', '', baseUrl + fragments[0] + '-L' + line);
        } else {
          const p = $("#L" + line).attr('id', '');
          window.history.pushState('', '', baseUrl + '#L' + line);
          p.attr('id','L' + line);
        }
        $("#branchCtrlWrapper .btn .muted").text("tree:");
        $("#branchCtrlWrapper .btn .strong").text("@latestCommit.id.substring(0, 10)");
        getSelection().empty();
        updateHighlighting();
      }).appendTo(pre);
      updateHighlighting();
    } else {
      // Maybe code view is not initialized yet. Retry until succeed.
      setTimeout(updateSourceLineNum, 300);
    }
  }
  const repository = $('.blame-action').data('repository');
  $('.blame-action').click(function(e){
    if(history.pushState && $('pre.prettyprint.no-renderable').length){
      e.preventDefault();
      history.pushState(null, null, this.href);
      updateBlame();
    }
  });

  function updateBlame(){
    const m = /\/(blame|blob)(\/.*)$/.exec(location.href);
    const mode = m[1];
    $('.blame-action').toggleClass("active", mode=='blame').attr('href', repository + (m[1] == 'blame' ? '/blob' : '/blame') + m[2]);
    if(pre.parents("div.box-content-bottom").find(".blame").length){
      pre.parent().toggleClass("blame-container", mode == 'blame');
      updateSourceLineNum();
      return;
    }
    if(mode == 'blob'){
      updateSourceLineNum();
      return;
    }
    $(document.body).toggleClass('no-box-shadow', document.body.style.boxShadow === undefined);
    $('.blame-action').addClass("active");
    const base = $('<div class="blame">').css({height: pre.height()}).prependTo(pre.parents("div.box-content-bottom"));
    base.parent().addClass("blame-container");
    updateSourceLineNum();
    $.get($('.blame-action').data('url')).done(function(data){
      let blame = data.blame;
      let lastDiv;
      const now = new Date().getTime();
      const index = [];
      for(let i = 0; i < blame.length; i++){
        for(let j = 0; j < blame[i].lines.length; j++){
          index[blame[i].lines[j]] = blame[i];
        }
      }

      $('pre.prettyprint ol.linenums li').each(function(i, e){
        const p = $(e).position();
        const h = $(e).height();
        if(blame == index[i]){
          lastDiv.css("min-height",(p.top + h + 1) - lastDiv.position().top);
        } else {
          $(e).addClass('blame-sep')
          blame = index[i];
          const sha = $('<div class="blame-sha">')
             .append($('<a>').attr("href", data.root + '/commit/' + blame.id).text(blame.id.substr(0, 7)));
          if (blame.prev) {
             sha.append($('<br />'))
             .append($('<a class="muted-link">').text('prev').attr("href", data.root + '/blame/' + blame.prev + '/' + (blame.prevPath || data.path)));
          }
          lastDiv = $('<div class="blame-info">')
           .addClass('heat' + Math.min(10, Math.max(1, Math.ceil((now - blame.commited) / (24 * 3600 * 1000 * 70)))))
           .toggleClass('blame-last', blame.id == data.last)
           .data('line', (i + 1))
           .css({
             "top"        : p.top + 'px',
             "min-height" : h + 'px'
           })
           .append(sha)
           .append($(blame.avatar).addClass('avatar').css({"float": "left"}))
           .append($('<div class="blame-commit-title">').text(blame.message))
           .append($('<div class="muted">').html(blame.author + " authed " + blame.authed))
           .appendTo(base);
        }
      });
    });
    return false;
  };

  $(document).on('expanded.pushMenu collapsed.pushMenu', function(e){
    setTimeout(updateBlame, 300);
  });

  updateBlame();
});

let scrolling = false;

/**
 * Highlight lines which are specified by URL hash.
 */
function updateHighlighting() {
  const hash = location.hash;
  const isDark = @{highlighterTheme.contains("dark").toString};
  if (hash.match(/#L\d+(-L\d+)?/)) {
    if (isDark) {
      $('li.highlight-dark').removeClass('highlight-dark');
    } else {
      $('li.highlight').removeClass('highlight');
    }
    const fragments = hash.substr(1).split('-');
    if (fragments.length == 1) {
      if (isDark) {
        $('#' + fragments[0]).addClass('highlight-dark');
      } else {
        $('#' + fragments[0]).addClass('highlight');
      }
      if(!scrolling){
        $(window).scrollTop($('#' + fragments[0]).offset().top);
      }
    } else if(fragments.length > 1){
      const start = parseInt(fragments[0].substr(1));
      const end   = parseInt(fragments[1].substr(1));
      for (let i = start; i <= end; i++) {
        if (isDark) {
          $('#L' + i).addClass('highlight-dark');
        } else {
          $('#L' + i).addClass('highlight');
        }
      }
      if (!scrolling) {
        $(window).scrollTop($('#' + fragments[0]).offset().top);
      }
    }
  }
  scrolling = true;
}
</script>
